# Author: Chris Moyer
from marajo.exceptions import NotFound
from marajo.appengine.handlers import RequestHandler
from marajo.appengine.api import users

import boto
from boto.utils import find_class
from boto.sdb.db.model import Model
from boto.sdb.db.key import Key
from boto.sdb.db.property import Property
from boto.exception import SDBPersistenceError

import re
import simplejson as json

import logging
log = logging.getLogger("marajo.sdb")

class SDBHandler(RequestHandler):
    """
    SDB Handler Base class
    """
    sdb_class = None
    template = None
    list_template = None

    def __init__(self, config, request, response):
        RequestHandler.__init__(self, config, request, response)

    def get(self):
        """
        Get or List all of these objects
        """
        obj = self.getObject()
        if obj:
            # get
            self.display(self.template, {"obj": self.format_object(obj)})
        else:
            # list
            json_objects = []
            for obj in self.sdb_class.find():
                json_objects.append(self.format_object(obj))
            self.display(self.list_template, {"objects": json_objects})
    
    def post(self):
        """
        Save or update object to the DB
        """
        obj = self.getObject()
        if obj:
            log.info("===== %s Update %s =====" % (users.get_current_user(self.session).username, self.sdb_class.__class__.__name__))
            self.update(obj, self.request.POST)
        else:
            log.info("===== %s Add %s =====" % (users.get_current_user(self.session).username, self.sdb_class.__class__.__name__))
            self.add(self.request.POST)
        log.info("========================")
        self.response.write(json.dumps(self.format_object(obj)))

    def update(self, obj, params):
        """
        Update our object
        @param params: Dictionary of params to set on this object
        @type params: dict
        """
        props = {}
        for p in obj.properties():
            props[p.name] = p
        for param in params.keys():
            if props.has_key(param):
                prop = props[param]
                if prop.data_type == list:
                    val = [self.decode(prop.item_type, v) for v in params.getall(param)]
                    log.info("%s: %s" % (param, str(val)))
                elif prop.data_type == Key:
                    val = self.decode(prop.reference_class, params.getone(param))
                    log.info("%s: %s" % (param, str(val.id)))
                else:
                    val = self.decode(prop.data_type, params.getone(param))
                    log.info("%s: %s" % (param, str(val)))
                setattr(obj, param, val)
        obj.save()
        self.response.content_type = "text/x-json"


    def add(self, params):
        """
        Add an object to the DB
        @param params: Dictionary of params to set on this object
        @type params: dict
        """
        obj = self.sdb_class()
        return self.update(obj, params)

    def delete(self):
        """
        Delete a given object
        """
        obj = self.getObject();
        obj.delete()
        log.info("%s Deleted object %s" % (users.get_current_user(self.session).username, obj.id))


    def format_object(self, obj):
        """
        Turn this object into a stripped down dictionary object so it can be
        easily written out via the json module
        @param obj: The Object to format
        @type obj: Model

        @return: This object as a dict
        @rtype: dict
        """
        log.debug("format_object: %s" % obj)
        if not isinstance(obj, object):
            return str(obj)
        json_obj = {}
        if hasattr(obj, "properties"):
            for p in obj.properties():
                log.debug("Formatting Model Object")
                if p.__class__.__name__[0] == "_":
                    continue
                try:
                    val = getattr(obj, p.name)
                except:
                    log.error("Error fetching attribute: %s[%s]" % (obj, p.name))
                    val = None
                if isinstance(val, object):
                    log.debug("\tval: %s(%s)" % (val, val.__class__.__name__))
                if isinstance(val, dict):
                    val = {}
                    for key in val:
                        val[key] = self.format_object(dict[key])
                elif isinstance(val, list):
                    json_val = []
                    for item in val:
                        log.debug("\t\titem: %s" % item)
                        json_val.append(self.format_object(item))
                    val = json_val
                else:
                    val = self.format_object(val)
                json_obj[p.name] = val
            json_obj['id'] = obj.id
            return json_obj
        else:
            return obj

    def getObject(self):
        """
        Get the object that this URI points to, or None if they don't point to one
        If we point to a URI that doesn't exist, we toss a NotFound error
        """
        obj = None
        if(self.request.match_groups and len(self.request.match_groups[0]) > 0):
            obj_id = self.request.match_groups[0].strip("/") # Remove leading and trailing "/" if they exist
            try:
                obj = self.sdb_class.get_by_ids(obj_id)
            except SDBPersistenceError, e:
                raise NotFound()

        return obj

    def decode(self, type, value):
        """
        Decode a string value sent by the user into actual things
        """
        if value:
            if Model in type.mro():
                return type.get_by_ids(value)
            elif type == bool:
                if value.lower() == 'true':
                    return True
                else:
                    return False
            else:
                return type(value)
        return value
